#include "stdafx.h"
#include "nBodyPhysicsScenarioModule.hpp"

#include "Namespaces.h"

nBodyPhysicsScenarioModule::nBodyPhysicsScenarioModule(void) {

}

void nBodyPhysicsScenarioModule::Start(void) {

	Debug_Log("[nBodyPhysicsScenarioModule] Beginning Initialization");

	auto FGB = FlightGlobals::Bodies ? FlightGlobals::Bodies : FlightGlobals::fetch->bodies;

	if (!FGB) {

		PDebug::Error("[nBodyPhysicsScenarioModule] Unable to get FlightGlobals::Bodies");

	}

	int i = 0, Length = FlightGlobals::Bodies->Count;	

	this->m_Bodies = gcnew array<CelestialBodyInfo ^>(Length);
	this->m_UpdateBodyTimesData = gcnew array<WorkerData ^>(Length);
	this->m_UpdateBodyTimesResetEvent = gcnew array<ManualResetEvent ^>(Length);

	for each (CelestialBody ^Body in FlightGlobals::Bodies) {

		this->m_UpdateBodyTimesData[i] = gcnew WorkerData(this->m_Bodies[i] = gcnew CelestialBodyInfo(Body),
				this->m_UpdateBodyTimesResetEvent[i] = gcnew ManualResetEvent(false));
		++i;

	}

	Debug_Log("[nBodyPhysicsScenarioModule] Initialization Complete");

}

void nBodyPhysicsScenarioModule::FixedUpdate(void) {

	Debug_Log("[nBodyPhysicsScenarioModule] FixedUpdate()");

	static int Iterations = 0;
	int NewIterations = int(Math::Ceiling(TimeWarp::CurrentRate));

	if (NewIterations != Iterations) {

		Debug_Log("[nBodyPhysicsScenarioModule] Iteration count changed");

		Iterations = NewIterations;
		this->m_Times = gcnew array<double>(Iterations);

	}

	double DeltaTime = double(TimeWarp::deltaTime) / double(Iterations);
	double NewTime = Planetarium::GetUniversalTime();

	Debug_Log("[nBodyPhysicsScenarioModule] Updating time array");

	for (int i = 1; i < Iterations; ++i) {
			
		this->m_Times[i] = NewTime;
		NewTime += DeltaTime;
	
	}	

	Debug_Log("[nBodyPhysicsScenarioModule] Time array update complete");

	if (Iterations == 1) {

		Debug_Log("[nBodyPhysicsScenarioModule] Updating celestial body positions (non-threaded)");

		for each (auto Body in this->m_Bodies) {

			Body->UpdatePositions(this->m_Times);

		}

	} else {

		Debug_Log("[nBodyPhysicsScenarioModule] Updating celestial body positions (threaded)");

		for each (auto WD in this->m_UpdateBodyTimesData) {

			WD->TaskReady();
			ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &nBodyPhysicsScenarioModule::Worker_UpdateBodyTimes), WD);

		}

		WaitHandle::WaitAll(this->m_UpdateBodyTimesResetEvent);

	}

	Debug_Log("[nBodyPhysicsScenarioModule] Celestial body position Update complete");
	Debug_Log("[nBodyPhysicsScenarioModule] Generating ship list");


	auto FGV = FlightGlobals::Vessels ? FlightGlobals::Vessels : FlightGlobals::fetch->vessels;
	int Length = FGV->Count;
	auto MRE = gcnew array<ManualResetEvent ^>(Length);

	for (int i = 0; i < Length; ++i) {

		ThreadPool::QueueUserWorkItem(gcnew WaitCallback(this, &nBodyPhysicsScenarioModule::Worker_ProcessVessel),
				gcnew WorkerData(FGV[i], MRE[i] = gcnew ManualResetEvent(false)));

	}
	WaitHandle::WaitAll(MRE);




}

void nBodyPhysicsScenarioModule::Worker_UpdateBodyTimes(object ^Data) {

	auto WD = dynamic_cast<WorkerData ^>(Data);
	dynamic_cast<CelestialBodyInfo ^>(WD->Data)->UpdatePositions(this->m_Times);
	WD->TaskComplete();

}

void nBodyPhysicsScenarioModule::Worker_ProcessVessel(object ^Data) {

	auto WD = dynamic_cast<WorkerData ^>(Data);
	auto V = dynamic_cast<Vessel ^>(WD->Data);

	if ((V->vesselType != VesselType::Debris) && !V->checkLanded() && !V->checkSplashed()) {



	}

	WD->TaskComplete();

}

void nBodyPhysicsScenarioModule::OnDestroy(void) {

}